[MacBook] Pythonでウィンドウを操作する(PyWinCtl)
こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、Pythonを使ってMacBook上で軽いRPA的なこと(ウィンドウの操作)がしたかったので、方法を調べてみました。
環境
$ hostinfo Mach kernel version: Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 Kernel configured for up to 8 processors. 4 processors are physically available. 4 processors are logically available. Processor type: x86_64h (Intel x86-64h Haswell) Processors active: 0 1 2 3 4 5 6 7 Primary memory available: 16.00 gigabytes Default processor set: 517 tasks, 2476 threads, 8 processors Load average: 4.55, Mach factor: 4.66 $ python --version Python 3.9.6
やってみた
今回試してみたのはPyWinCtlです。
PyWinCtlはPyGetWindowのフォークです。PyGetWindowでサポートしているのはMS Windowsのみですが、PyWinCtlではMacOSやLinuxについてもサポート対象となっています。
インストール、Permission設定
PyWinCtlをインストールします。(環境によってはインストール量が結構多くなります。)
$ pip install pywinctl
Pythonを実行するアプリケーションにMacのアクセス権限を付与します。今回はVS Codeです。
$ python >>> import pywinctl as pwc # Permission設定前ならFalseになる。 >>> pwc.checkPermissions() False # 適当なコマンドでPermission設定を出す。 >>> pwc.getActiveWindow() # Permission設定済みならTrueになる。 >>> pwc.checkPermissions() True
次のようなPermission設定が出るので許可します。
ウィンドウを操作してみる
現在、次のようにFinderアプリのウィンドウがアクティブかつ最大化となっています。また外付けディスプレイを2枚使用しています。
現在アクティブなウィンドウの一覧です。MacBook本体+外付け2枚で合計3つのアクティブウィンドウが取得できています。
>>> pwc.getAllWindows() [MacOSWindow(hWnd=<NSRunningApplication: 0x7fb2ac068bb0 (com.microsoft.VSCode - 796) LSASN:{hi=0x0;lo=0x24024}>), MacOSWindow(hWnd=<NSRunningApplication: 0x7fb2ac0b4ed0 (com.apple.finder - 803) LSASN:{hi=0x0;lo=0x2e02e}>), MacOSWindow(hWnd=<NSRunningApplication: 0x7fb2ac0cdf00 (com.google.Chrome - 28691) LSASN:{hi=0x0;lo=0x192192}>)]
getAllTitles
で、現在アクティブなウィンドウのタイトル一覧を取得できます。Finderでデスクトップを開いているのでDesktop
となっています。
>>> pwc.getAllTitles() ['Python — devio', 'Desktop', 'クラスメソッド発「やってみた」系技術メディア | DevelopersIO - Google Chrome']
getWindowsWithTitle
で、タイトルを指定して特定のウィンドウを取得できます。
>>> pwc.getWindowsWithTitle('Desktop') [MacOSWindow(hWnd=<NSRunningApplication: 0x7fb2ac0b4ed0 (com.apple.finder - 803) LSASN:{hi=0x0;lo=0x2e02e}>)]
restore
で、ウィンドウの最大化を解除できます。
>>> win = pwc.getWindowsWithTitle('Desktop')[0] >>> win.restore() True
初回の場合はPremissionを求められます。
最大化を解除した状態であれば、hide
で、ウィンドウを最小化できます。
>>> win.hide() False
maximize
で、ウィンドウを最大化できます。
>>> win.maximize() True
activate
で、指定のウィンドウをアクティブにできます。
>>> win.activate() True
例えば実行前のアクティブウィンドウがGoogle Chromeなら、
実行するとFinder(デスクトップ)に切り替わります。
注意点
アクティブでないウィンドウは取得できない
ちなみにアクティブでないウィンドウはgetWindowsWithTitle
を使っても取得できません。
# Finder(デスクトップ)が非アクティブの場合 >>> desktop = pwc.getWindowsWithTitle('Desktop')[0] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range
よって、スクリプトなどで自動化したい場合はあらかじめpickle
でファイル化するなどしてウィンドウのオブジェクトを保持しておきたいところですが、Objective-Cオブジェクトのため出来ないようです。
>>> import pickle >>> with open('pic.bin', 'wb') as p: ... pickle.dump(skitch, p) ... Traceback (most recent call last): File "<stdin>", line 2, in <module> TypeError: Cannot pickle Objective-C objects
よってウィンドウオブジェクトの保存時/復元時にPyObjCなどを使用してObjective-Cオブジェクトとバイナリ間でブリッジしてあげる必要がありそうです。
おわりに
Pythonを使ってMacBook上で軽いRPA的なこと(ウィンドウの操作)がしたかったので、方法を調べてみました。
当初はさほど難しくないと思っていましたが、Apple製品特有のPermissionのガードの高さゆえ、同じPythonライブラリを使用して他のOSで出来ることがMacOSでは出来ないと、いうことが調べていてよくありました。注意したいですね。
以上